package main

import (
	"fmt"
	"unsafe"
)

/*
基本数据类型
bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32,represents a Unicode code point
float32 float64
complex64 complex128
*/
func main() {
	//1.布尔类型
	var b1 bool
	b1 = true
	fmt.Printf("b1: %T,%t\n", b1, b1)
	b2 := false
	fmt.Printf("b2: %T,%t\n", b2, b2)
	//bool类型占用存储空间是1个字节
	fmt.Println("b2 的占用空间 =", unsafe.Sizeof(b2)) //1

	//2.整数
	var i1 int8
	i1 = 100
	fmt.Println(i1)
	var i2 uint8
	i2 = 200
	fmt.Println(i2)

	var i3 int
	i3 = 1000
	fmt.Println(i3)
	//语法角度：int，int64不认为是同一种类型
	//var i4 int64
	//i4 = i3 //cannot use i3 (type int) as type int64 in assignment

	var i5 uint8
	i5 = 100
	var i6 byte
	i6 = i5
	fmt.Println(i5, i6)

	var i7 = 100
	fmt.Printf("i7: %T,%d\n", i7, i7)

	// unsafe 包提供的 SizeOf 函数
	var ao, bo = int(5), uint(6)
	var p uintptr = 0x12345678
	fmt.Println(unsafe.Sizeof(ao)) // 8
	fmt.Println(unsafe.Sizeof(bo)) // 8
	fmt.Println(unsafe.Sizeof(p))  // 8

	//整型溢出
	var num int8 = 127
	num += 1
	fmt.Println(num) //期望结果128,实际-128

	var num2 uint8 = 1
	num2 -= 2
	fmt.Println(num2) //期望结果-1,实际255

	//通过标准库 fmt 包的格式化输出函数，将一个整型变量输出为不同进制的形式
	var ab int8 = 59
	fmt.Printf("二进制: %b\n", ab)        //输出二进制：111011
	fmt.Printf("十进制: %d\n", ab)        //输出十进制：59
	fmt.Printf("八进制: %o\n", ab)        //输出八进制：73
	fmt.Printf("八进制(带0o前缀): %O\n", ab) //输出八进制(带0o前缀)：0o73
	fmt.Printf("十六进制(小写): %x\n", ab)   //输出十六进制(小写)：3b
	fmt.Printf("十六进制(大写): %X\n", ab)   //输出十六进制(大写)：3B

	//浮点
	var f1 float32
	f1 = 3.14
	var f2 float64
	f2 = 4.67
	fmt.Printf("f1: %T,%.2f\n", f1, f1)
	fmt.Printf("f2: %T,%.3f\n", f2, f2)
	fmt.Println(f1)

	var f3 = 2.55
	fmt.Printf("f3: %T\n", f3)

	//float32精度问题
	var x1 float32 = 16777216.0
	var x2 float32 = 16777217.0
	fmt.Println("x1==x2? ", x1 == x2) // true

	//用十进制表示的浮点值形式
	float1 := 3.1415
	float2 := .15 // 整数部分如果为0，整数部分可以省略不写
	float3 := 81.80
	float4 := 82. // 小数部分如果为0，小数点后的0可以省略不写

	//十进制的科学计数法形式（e/E 代表的幂运算的底数为 10）
	float5 := 6674.28e-2 // 6674.28 * 10^(-2) = 66.742800
	float6 := .12345e+5  // 0.12345 * 10^5 = 12345.000000

	//十六进制科学计数法形式（ p/P 代表的幂运算的底数为 2）
	float7 := 0x2.p10  // 2.0 * 2^10 = 2048.000000
	float8 := 0x1.Fp+0 // 1.9375 * 2^0 = 1.937500

	fmt.Println(float1, float2, float3, float4, float5, float6, float7, float8) //3.1415 0.15 81.8 82 66.7428 12345 2048 1.9375

	//通过 %f 可以输出浮点数最直观的原值形式
	var f9 float64 = 123.45678
	fmt.Printf("%f\n", f9) // 123.456780
	//将浮点数输出为科学计数法形式
	fmt.Printf("%e\n", f9) // 1.234568e+02 //十进制的科学计数法
	fmt.Printf("%x\n", f9) // 0x1.edd3be22e5de1p+06 //十六进制的科学计数法

	//复数类型
	//第一种，可以通过复数字面值直接初始化一个复数类型变量
	var c1 = 5 + 6i
	var d1 = 0o123 + .12345e+5i // 83+12345i
	fmt.Println(c1, d1)

	//第二种，通过complex 函数创建一个 complex128 类型值
	var c2 = complex(5, 6)             // 5 + 6i
	var d2 = complex(0o123, .12345e+5) // 83+12345i
	fmt.Println(c2, d2)

	//第三种，通过 real 和 imag 函数来获取一个复数的实部与虚部，返回值为一个浮点类型
	var c3 = complex(5, 6) // 5 + 6i
	r := real(c3)          // 5.000000
	i := imag(c3)          // 6.000000
	fmt.Println(r, i)

	//4.数据类型转换
	var a int8
	a = 10

	var b int16
	b = int16(a)
	fmt.Println(a, b)

	k1 := 4.83
	var c int
	c = int(k1)
	fmt.Println(k1, c)

	k1 = float64(a)
	fmt.Println(k1, a)

	//t1 := true
	//a = int8(t1) //cannot convert t1 (type bool) to type int8

	sum := f1 + 100
	fmt.Printf("%T,%f\n", sum, sum)

	//自定义数据类型
	//MyInt的数值性质与 int32 完全相同
	type MyInt int32

	//但仍然是完全不同的两种类型，无法直接让它们相互赋值或放在同一个运算中直接计算
	var m int = 5
	var n int32 = 6
	// var a MyInt = m // 错误：在赋值中不能将m（int类型）作为MyInt类型使用
	// var a MyInt = n // 错误：在赋值中不能将n（int32类型）作为MyInt类型使用

	//需要显示的转换类型才可以
	var ax1 MyInt = MyInt(m) // ok
	var ax2 MyInt = MyInt(n) // ok
	fmt.Println(ax1, ax2)

	//也可以通过类型别名（Type Alias）来自定义数值类型，此时定义的新类型与原类型没有区别
	type YourInt = int32
	var nx1 int32 = 6
	var nx2 YourInt = n // ok
	fmt.Println(nx1, nx2)

	//指针对应的是变量在内存中的存储位置,也就是说 指针的值就是变量的内存地址
	dat := 10
	datP := &dat //data的指针,结果是 0xc00001e1a0
	// datP = datP + 1 //报错:指针不支持运算
	datV := *datP //dataP指针对应的值,结果是 10
	fmt.Println(datP, datV)
	fmt.Printf("%T %T \n", datP, datV) //*int int

	//判断数据类型
	data := "个人信息"
	fmt.Println(getVarType(data)) //string
}

func getVarType(args interface{}) string {
	switch args.(type) {
	case nil:
		return "nil"
	case int:
		return "int"
	case int64:
		return "int64"
	case float64:
		return "float64"
	case bool:
		return "bool"
	case string:
		return "string"
	case func(int) float64:
		return "func(int)"
	default:
		return "unknown"
	}
}
